home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / HippoDraw / HippoDrawSrc1.1 / Hippo.subproj / Group.m < prev    next >
Encoding:
Text File  |  1992-04-25  |  8.2 KB  |  350 lines

  1. #import "Group.h"
  2. #import "GraphicView.h"
  3. #import <appkit/Application.h>
  4. #import <appkit/NXImage.h>
  5. #import <dpsclient/wraps.h>
  6. #import <objc/List.h>
  7.  
  8. /* Optimally viewed in a wide window.  Make your window big enough so that this comment fits entirely on one line w/o wrapping. */
  9.  
  10. #define GROUP_CACHE_THRESHOLD 4
  11.  
  12. @implementation Group : Graphic
  13. /*
  14.  * This Graphic is used to create heirarchical groups of other Graphics.
  15.  * It simply keeps a list of all the Graphics in the group and resizes
  16.  * and translates them as the Group object itself is resized and moved.
  17.  * It also passes messages sent to the Group onto its members.
  18.  *
  19.  * For efficiency, we cache the group whenever it passes the caching
  20.  * threshold.  Thus, grouping becomes a tool to let the user have some
  21.  * control over the memory/speed tradeoff (which can be different
  22.  * depending on the kind of drawing the user is making).
  23.  */
  24.  
  25. /* Factory method */
  26.  
  27. - initList:list
  28. /*
  29.  * Creates a new grouping with list containing the list of Graphics
  30.  * in the group.  Groups of Groups is perfectly allowable.  We have
  31.  * to keep track of the largest linewidth in the group as well as
  32.  * whether any of the elements of the group have arrows since both
  33.  * of those attributes affect the extended bounds of the Group.
  34.  * We set any objects which might be cacheing (notably subgroups of
  35.  * this group) to be not cacheable since it is no use for them to
  36.  * cache themselves when we are caching them as well.
  37.  */
  38. {
  39.     id g;
  40.     int i;
  41.     NXRect r;
  42.  
  43.     [super init];
  44.  
  45.     i = [list count];
  46.     g = [list objectAt:--i];
  47.     [g getBounds:&bounds];
  48.     gFlags.arrow = [g lineArrow];
  49.     linewidth = [g lineWidth];
  50.     while (i) {
  51.     g = [list objectAt:--i];
  52.     [g getBounds:&r];
  53.     [g setCacheable:NO];
  54.     if (!r.size.width) r.size.width = 1.0;
  55.     if (!r.size.height) r.size.height = 1.0;
  56.     NXUnionRect(&r, &bounds);
  57.     if (!gFlags.arrow && [g lineArrow]) gFlags.arrow = [g lineArrow];
  58.     if ([g lineWidth] > linewidth) linewidth = [g lineWidth];
  59.     }
  60.  
  61.     components = list;
  62.     lastRect = bounds;
  63.  
  64.     return self;
  65. }
  66.  
  67. - free
  68. {
  69.     [components freeObjects];
  70.     [components free];
  71.     [cache free];
  72.     return [super free];
  73. }
  74.  
  75. /* Public methods */
  76.  
  77. - transferSubGraphicsTo:list at:(int)position
  78. {
  79.     int i;
  80.  
  81.     i = [components count];
  82.     while (i--) [list insertObject:[[components removeLastObject] setCacheable:YES] at:position];
  83.  
  84.     return self;
  85. }
  86.  
  87. /* Methods overridden from superclass */
  88.  
  89. - setCacheable:(BOOL)flag
  90. /*
  91.  * Sets whether we do caching of this Group or not.
  92.  */
  93. {
  94.     dontCache = flag ? NO : YES;
  95.     if (dontCache) {
  96.     [cache free];
  97.     cache = nil;
  98.     }
  99.     return self;
  100. }
  101.  
  102. - (BOOL)isCacheable
  103. {
  104.     return !dontCache;
  105. }
  106.  
  107. - draw
  108. /*
  109.  * Individually scales and translates each Graphic in the group and draws
  110.  * them.  This is done this way so that ungrouping is trivial.  Note that
  111.  * if we are caching, we need to take the extra step of translating
  112.  * everything to the origin, drawing them in the cache, then translating
  113.  * them back.
  114.  */
  115. {
  116.     int i;
  117.     Graphic *g;
  118.     NXRect eb, b;
  119.     float sx = 1.0, sy = 1.0, tx, ty;
  120.     BOOL changed, changedSize, caching = NO;
  121.  
  122.     if (bounds.size.width < 1.0 || bounds.size.height < 1.0 || !components) return self;
  123.  
  124.     changedSize = lastRect.size.width != bounds.size.width || lastRect.size.height != bounds.size.height;
  125.     changed = changedSize || lastRect.origin.x != bounds.origin.x || lastRect.origin.y != bounds.origin.y;
  126.  
  127.     if ((changedSize || !cache) && NXDrawingStatus == NX_DRAWING) {
  128.     [cache free];
  129.     cache = nil;
  130.     if (DrawStatus != Resizing && !dontCache && [components count] > GROUP_CACHE_THRESHOLD) {
  131.         caching = YES;
  132.         [self getExtendedBounds:&eb];
  133.         cache = [[NXImage allocFromZone:[self zone]] initSize:&eb.size];
  134.         [cache lockFocus];
  135.         [[[NXApp focusView] window] reenableDisplay];    /* workaround for AppKit bug */
  136.         b = eb; b.origin.x = b.origin.y = 0.0;
  137.         PSsetalpha(0.0);
  138.         PSsetgray(NX_WHITE);
  139.         NXRectFill(&b);
  140.         PSsetalpha(1.0);
  141.     }
  142.     }
  143.  
  144.     if (changedSize) {
  145.     sx = bounds.size.width / lastRect.size.width;
  146.     sy = bounds.size.height / lastRect.size.height;
  147.     }
  148.  
  149.     i = [components count];
  150.     while (i) {
  151.     g = [components objectAt:--i];
  152.     if (changed) {
  153.         [g getBounds:&b];
  154.         tx = (bounds.origin.x + ((b.origin.x - lastRect.origin.x) / lastRect.size.width * bounds.size.width)) - b.origin.x;
  155.         ty = (bounds.origin.y + ((b.origin.y - lastRect.origin.y) / lastRect.size.height * bounds.size.height)) - b.origin.y;
  156.         b.origin.x = b.origin.x + tx;
  157.         b.origin.y = b.origin.y + ty;
  158.         b.size.width = b.size.width * sx;
  159.         b.size.height = b.size.height * sy;
  160.         [g setBounds:&b];
  161.     } else if (caching) {
  162.         [g getBounds:&b];
  163.     }
  164.     if (NXDrawingStatus != NX_DRAWING || !cache || caching) {
  165.         if (caching) {
  166.         b.origin.x -= eb.origin.x;
  167.         b.origin.y -= eb.origin.y;
  168.         [g setBounds:&b];
  169.         }
  170.         [g setGraphicsState];    /* does a gsave ... */
  171.         [g draw];
  172.         PSgrestore();        /* ... so we need this grestore */
  173.         if (caching) {
  174.         b.origin.x += eb.origin.x;
  175.         b.origin.y += eb.origin.y;
  176.         [g setBounds:&b];
  177.         }
  178.     }
  179.     }
  180.  
  181.     if (cache && NXDrawingStatus == NX_DRAWING) {
  182.     if (caching) {
  183.         [cache unlockFocus];
  184.     } else {
  185.         [self getExtendedBounds:&eb];
  186.     }
  187.     [cache composite:NX_SOVER toPoint:&eb.origin];
  188.     }
  189.  
  190.     lastRect = bounds;
  191.  
  192.     return self;
  193. }
  194.  
  195. - (BOOL)hit:(const NXPoint *)point
  196. /*
  197.  * Gets a hit if any of the items in the group gets a hit.
  198.  */
  199. {
  200.     id g;
  201.     int i;
  202.     NXPoint p;
  203.     float px, py;
  204.  
  205.     if ([super hit:point]) {
  206.     if (components) {
  207.         p = *point;
  208.         px = (p.x - bounds.origin.x) / bounds.size.width;
  209.         p.x = px * lastRect.size.width + lastRect.origin.x;
  210.         py = (p.y - bounds.origin.y) / bounds.size.height;
  211.         p.y = py * lastRect.size.height + lastRect.origin.y;
  212.         i = [components count];
  213.         while (i) {
  214.         g = [components objectAt:--i];
  215.         if ([g hit:&p]) return YES;
  216.         }
  217.     } else {
  218.         return YES;
  219.     }
  220.     }
  221.  
  222.     return NO;
  223. }
  224.  
  225. /* Compatibility methods */
  226.  
  227. - replaceWithImage
  228. /*
  229.  * Since we got rid of Tiff and PSGraphic and replaced them
  230.  * with the unified Image graphic, we need to go through our
  231.  * list and replace all of them with an Image graphic.
  232.  */
  233. {
  234.     int i;
  235.     id g, newg;
  236.  
  237.     for (i = [components count]-1; i >= 0; i--) {
  238.     g = [components objectAt:i];
  239.     newg = [g replaceWithImage];
  240.     if (g != newg) {
  241.         if (g) {
  242.         [components replaceObjectAt:i with:newg];
  243.         } else {
  244.         [components removeObjectAt:i];
  245.         }
  246.     }
  247.     }
  248.  
  249.     return self;
  250. }
  251.  
  252. /* Group must override all the setting routines to forward to components */
  253.  
  254. - makeGraphicsPerform:(SEL)aSelector with:(const void *)anArgument
  255. {
  256.     [components makeObjectsPerform:aSelector with:(id)anArgument];
  257.     [cache free];
  258.     cache = nil;
  259.     return self;
  260. }
  261.  
  262. - changeFont:sender
  263. {
  264.     return [self makeGraphicsPerform:@selector(changeFont:) with:sender];
  265. }
  266.  
  267. - font
  268. {
  269.     int i;
  270.     id gfont, font = nil;
  271.  
  272.     i = [components count];
  273.     while (i--) {
  274.     gfont = [[components objectAt:i] font];
  275.     if (gfont) {
  276.         if (font && font != gfont) {
  277.         font = nil;
  278.         break;
  279.         } else {
  280.         font = gfont;
  281.         }
  282.     }
  283.     }
  284.  
  285.     return font;
  286. }
  287.  
  288. - setLineWidth:(const float *)value
  289. {
  290.     return [self makeGraphicsPerform:@selector(setLineWidth:) with:value];
  291. }
  292.  
  293. - setGray:(const float *)value
  294. {
  295.     return [self makeGraphicsPerform:@selector(setGray:) with:value];
  296. }
  297.  
  298. - setFillColor:(NXColor *)aColor
  299. {
  300.     return [self makeGraphicsPerform:@selector(setFillColor:) with:aColor];
  301. }
  302.  
  303. - setFill:(int)mode
  304. {
  305.     return [self makeGraphicsPerform:@selector(setFill:) with:(void *)mode];
  306. }
  307.  
  308. - setLineColor:(NXColor *)aColor
  309. {
  310.     return [self makeGraphicsPerform:@selector(setLineColor:) with:aColor];
  311. }
  312.  
  313. - setLineCap:(int)value
  314. {
  315.     return [self makeGraphicsPerform:@selector(setLineCap:) with:(void *)value];
  316. }
  317.  
  318. - setLineArrow:(int)value
  319. {
  320.     return [self makeGraphicsPerform:@selector(setLineArrow:) with:(void *)value];
  321. }
  322.  
  323. - setLineJoin:(int)value
  324. {
  325.     return [self makeGraphicsPerform:@selector(setLineJoin:) with:(void *)value];
  326. }
  327.  
  328. /* Archiving methods */
  329.  
  330. - write:(NXTypedStream *)stream
  331. /*
  332.  * Just writes out the components.
  333.  */
  334. {
  335.     [super write:stream];
  336.     NXWriteTypes(stream, "@", &components);
  337.     return self;
  338. }
  339.  
  340. - read:(NXTypedStream *)stream
  341. {
  342.     [super read:stream];
  343.     NXReadTypes(stream, "@", &components);
  344.     lastRect = bounds;
  345.     return self;
  346. }
  347.  
  348. @end
  349.  
  350.